Skip to content

[Trimming] Fix startup crash when TypeMapAssemblyTarget attribute survives but target assembly is fully trimmed - #130589

Merged
jtschuster merged 9 commits into
mainfrom
copilot/fix-startup-crash-trimming
Jul 21, 2026
Merged

[Trimming] Fix startup crash when TypeMapAssemblyTarget attribute survives but target assembly is fully trimmed#130589
jtschuster merged 9 commits into
mainfrom
copilot/fix-startup-crash-trimming

Conversation

Copilot AI commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

In trimmed CoreCLR apps, a TypeMapAssemblyTarget attribute could survive trimming even when its named assembly was fully trimmed (all its TypeMap entries were conditional on trim targets that were never marked). At runtime, TypeMapLazyDictionary unconditionally calls Assembly.Load for every surviving attribute, crashing with FileNotFoundException.

Changes

Fix — ILLink TypeMapHandler.cs

  • TypeMapAssemblyTarget attributes are now only marked when their target assembly is also marked (has surviving entries or is otherwise kept).
  • Added _pendingAssemblyTargetsByAssembly: when a group is seen but the target assembly is not yet marked, the attribute is deferred here rather than immediately marked.
  • TriggerPendingAssemblyTargets() is internal and called only from MarkStep.MarkAssembly whenever an assembly is first marked — ensuring deferred attributes are flushed regardless of assembly visitation order.
  • A comment notes that this is a slight over-approximation: ideally the attribute would only be kept when the target assembly has a surviving TypeMapAttribute, but the added complexity is not justified for a narrow case.
  • If the target assembly can't be resolved in the linker input at all, the attribute is silently dropped (Assembly.Load would fail anyway).

Fix — MarkStep.cs

  • MarkAssembly calls _typeMapHandler.TriggerPendingAssemblyTargets(assembly) to flush any pending TypeMapAssemblyTarget attributes for the assembly being marked, regardless of the order in which TypeMap entries are visited.

Test

  • New test TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed with two dependency assemblies: a shared group-type assembly, and a conditional.dll with only conditional TypeMap entries (trim target never marked). Verifies via [RemovedAttributeInAssembly] and [RemovedAssembly] that the TypeMapAssemblyTarget attribute and the conditional assembly are both removed from the linked output.

…no surviving entries

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 12, 2026 22:23
@dotnet-policy-service dotnet-policy-service Bot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 12, 2026
….cs and assemblynative.cpp

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 12, 2026 22:27
Copilot AI changed the title [WIP] Fix startup crash caused by TypeMapAssemblyTarget attribute [Trimming] Fix startup crash when TypeMapAssemblyTarget attribute survives but target assembly is fully trimmed Jul 12, 2026
Copilot AI requested a review from AaronRobinsonMSFT July 12, 2026 22:28
Copilot AI temporarily deployed to copilot-pat-pool July 12, 2026 23:09 Inactive
Copilot AI temporarily deployed to copilot-pat-pool July 12, 2026 23:09 Inactive
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@copilot No changes should be done to CoreCLR itself. This is entirely a trimming issue. When the trimmer decides to remove a type based on the typemap reference algorithm, it should also remove the typemap attributes that reference it.

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 12, 2026 23:15

Copilot AI commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot No changes should be done to CoreCLR itself. This is entirely a trimming issue. When the trimmer decides to remove a type based on the typemap reference algorithm, it should also remove the t...

Reverted all changes to assemblynative.cpp in commit $(git rev-parse --short HEAD). The fix is now purely in the ILLink trimmer (TypeMapHandler.cs): TypeMapAssemblyTarget attributes are only marked when their target assembly has at least one surviving TypeMap/TypeMapAssociation entry, so dangling attributes are removed at trim time rather than relying on any CoreCLR defense.

@copilot No changes should be done to CoreCLR itself. This is entirely a trimming issue. When the trimmer decides to remove a type based on the typemap reference algorithm, it should also remove the t...

Reverted all changes to assemblynative.cpp in commit 516910c2. The fix is now purely in the ILLink trimmer (TypeMapHandler.cs): TypeMapAssemblyTarget attributes are only marked when their target assembly has at least one surviving TypeMap/TypeMapAssociation entry, so dangling attributes are removed at trim time rather than relying on any CoreCLR defense.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "0d63a4015f5a83231ef59e93277ff29fa2c82e1e",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "0e7b627c152a3c1885d1db498d81d01de55a3391",
  "last_reviewed_commit": "0d63a4015f5a83231ef59e93277ff29fa2c82e1e",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "0e7b627c152a3c1885d1db498d81d01de55a3391",
  "last_recorded_worker_run_id": "29682980805",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "0d63a4015f5a83231ef59e93277ff29fa2c82e1e",
      "review_id": 4730589612
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: In trimmed CoreCLR apps a TypeMapAssemblyTarget attribute could survive trimming even when its named target assembly was fully trimmed away (all its TypeMap entries were conditional on trim targets that were never marked). At runtime TypeMapLazyDictionary unconditionally calls Assembly.Load for every surviving attribute, so the residual attribute caused a FileNotFoundException startup crash. This is a real, clearly motivated correctness fix.

Approach: TypeMapHandler no longer marks a TypeMapAssemblyTarget attribute unconditionally when its group is seen. Instead it resolves the target assembly during TypeMapResolver.Resolve, stores it alongside the pending entry, and defers the decision to MarkAssemblyTargetIfReady: the attribute is marked only if the target assembly is (or becomes) marked, dropped if the target assembly cannot be resolved, and deferred in _pendingAssemblyTargetsByAssembly otherwise. MarkStep.MarkAssembly flushes deferred entries via TriggerPendingAssemblyTargets when any assembly is first marked, which removes the visitation-order dependency the reviewer flagged. The over-approximation (keeping the attribute whenever the target assembly is marked for any reason, not strictly when it retains a surviving TypeMapAttribute) is a reasonable, well-commented tradeoff for a narrow case.

Summary: The change is correct, self-consistent, and low-risk. The new test TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed reproduces the scenario with a separate group-type assembly plus a conditional.dll referenced only by name string, and now asserts both [RemovedAssembly("conditional.dll")] and [RemovedAttributeInAssembly("test", typeof(TypeMapAssemblyTargetAttribute<AllConditionalGroupType>))], giving explicit verification of the intended behavior. Prior reviewer feedback from jtschuster (call TriggerPendingAssemblyTargets from MarkStep, remove the now-redundant belt-and-suspenders call, add the over-approximation comment) has all been incorporated. The pre-existing unvalidated ConstructorArguments[0] read in TypeMapResolver.Resolve was explicitly deferred by the maintainer to a separate change and is out of scope here. No actionable findings; the fix looks good.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 126.5 AIC · ⌖ 10.6 AIC · ⊞ 10K

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8d5de1a2-a6e8-46fb-84d2-6a971d76419c
Copilot AI review requested due to automatic review settings July 20, 2026 21:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@jtschuster
jtschuster requested a review from jkoritzinsky July 20, 2026 21:08

@sbomer sbomer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please add Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt.

@jtschuster

Copy link
Copy Markdown
Member

@copilot please add Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt.

Co-authored-by: jtschuster <36744439+jtschuster@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 21, 2026 16:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because there is no eligible user to bill. To allow Copilot reviews on bot-authored pull requests, enable direct organization billing in your organization's Copilot settings.

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please add Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt.

Added Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt in commit a5eaec7c. The ILTrim ReflectionTests suite passes with the case recognized as an expected failure.

@copilot please add Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt.

Added Reflection.TypeMapAssemblyTargetRemovedWhenAllEntriesTrimmed to ILTrimExpectedFailures.txt in commit a5eaec7c. The ILTrim ReflectionTests suite passes with the case recognized as an expected failure.

Copilot AI requested a review from jtschuster July 21, 2026 16:53
@jtschuster
jtschuster requested a review from sbomer July 21, 2026 17:21
@jtschuster
jtschuster enabled auto-merge (squash) July 21, 2026 19:44
@jtschuster
jtschuster merged commit 25b1e7a into main Jul 21, 2026
127 of 129 checks passed
@jtschuster
jtschuster deleted the copilot/fix-startup-crash-trimming branch July 21, 2026 20:13
@github-project-automation github-project-automation Bot moved this to Done in AppModel Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Tools-ILLink .NET linker development as well as trimming analyzers linkable-framework Issues associated with delivering a linker friendly framework

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Trimming] Startup crash when TypeMapAssemblyTarget attribute kept but target assembly trimmed

6 participants